package com.symaster.common.test;

import cn.hutool.core.collection.ConcurrentHashSet;
import com.symaster.common.geom.entity.LineSegment;
import com.symaster.common.geom.entity.Point;
import com.symaster.common.geom.entity.Vector;
import com.symaster.common.geom.util.PointUtil;
import com.symaster.common.swing.FpsManager;
import com.symaster.common.swing.util.SwingUtil;

import javax.swing.*;
import java.awt.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author yinmiao
 * @version 2023/11/11
 */
public class HeredityTest3 {

    public static void main(String[] args) {
        Common.setup();

        Scene scene = new Scene();

        JFrame jFrame = new JFrame("HeredityTest3");
        jFrame.setPreferredSize(new Dimension(1600, 900));
        jFrame.setContentPane(scene);
        jFrame.pack();
        SwingUtil.toCenter(jFrame);
        jFrame.setVisible(true);

        new Thread(() -> {
            for (int i = 0; i < 1; i++) {

                Role role = new Predator();
                role.scene = scene;
                role.color = new Color(Common.RANDOM.nextInt(100), Common.RANDOM.nextInt(255), Common.RANDOM.nextInt(255));
                role.center = new Point(Common.RANDOM.nextDouble() * 1600, Common.RANDOM.nextDouble() * 900);
                role.size = 10f + (float) (Common.RANDOM.nextDouble() * 30.f);

                scene.addRole(role);

                // try {
                //     TimeUnit.SECONDS.sleep(1);
                // } catch (InterruptedException e) {
                //     throw new RuntimeException(e);
                // }
            }
        }).start();

        new Thread(() -> {
            while (true) {
                if (scene.roles.size() < 100) {
                    Role role = new Predator();
                    role.scene = scene;
                    role.hp = 0.0f;
                    role.center = new Point(Common.RANDOM.nextDouble() * 1600, Common.RANDOM.nextDouble() * 900);
                    role.color = new Color(Common.RANDOM.nextInt(100), Common.RANDOM.nextInt(255), Common.RANDOM.nextInt(255));
                    role.size = 10f + (float) (Common.RANDOM.nextDouble() * 30.f);

                    scene.addRole(role);
                }

                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();

    }

    public static class Scene extends JComponent {
        private final FpsManager fpsManager;
        private final ThreadPoolExecutor threadPool;
        public final Set<Role> roles;

        public Scene() {
            this.fpsManager = new FpsManager();
            this.threadPool = new ThreadPoolExecutor(1000, 1000, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

            this.roles = new ConcurrentHashSet<>();
        }

        public void addRole(Role role) {
            roles.add(role);
            threadPool.execute(role);
        }

        @Override
        public void paint(Graphics g) {
            super.paint(g);
            SwingUtil.antialiasingOn((Graphics2D) g);

            for (Role role : new HashSet<>(roles)) {
                g.setColor(role.color);

                float v = role.size / 2;

                int size = Math.round(role.size);
                int x = (int) Math.round(role.center.getX() - v);
                int y = (int) Math.round(role.center.getY() - v);

                g.fillOval(x, y, size, size);
            }

            fpsManager.paintRunnable(120, this::repaint);

            List<Role> collect = roles.stream().filter(e -> e.hp > 0).collect(Collectors.toList());
            double v = collect.stream().mapToDouble(e -> e.size).average().orElse(0.0);
            g.setColor(new Color(25, 25, 25));
            g.drawString(String.format("count:%s, avg:%.2f", collect.size(), v), 5, 15);
        }
    }

    /**
     * 生产者
     */
    public static class Producer extends Role {

        @Override
        public void exec() {

        }
    }

    /**
     * 捕食者
     */
    public static class Predator extends Role {

        @Override
        public void exec() {
            Set<Role> roles = aroundRole(perceivedDistance);

            List<Role> collect1 = roles.stream().sorted((o1, o2) -> {
                LineSegment lineSegment = new LineSegment(center, o1.center);
                double o1L = lineSegment.length();
                lineSegment.setEndPoint(o2.center);
                double o2L = lineSegment.length();
                return Double.compare(o1L, o2L);
            }).collect(Collectors.toList());

            /* 周围没有对象, 回血 */
            if (collect1.isEmpty()) {
                if (hp > 0.9) {
                    Vector translateVector = PointUtil.getTranslateVector(center, new Point(800, 450));
                    motion(translateVector);
                } else {
                    stop();
                }
                return;
            }

            List<Role> collect = collect1.stream().filter(e -> {
                if (e.hp <= 0) {
                    return false;
                }
                Vector translateVector = PointUtil.getTranslateVector(center, e.center);
                return translateVector.length() < (e.size / 2.0) + (size / 2.0) + 20;
            }).collect(Collectors.toList());

            if (!collect.isEmpty()) {
                Role role = collect.get(0);

                if (hp < 0.5f || (attackTime != null && time() - attackTime < 1000)) {

                    Vector translateVector = PointUtil.getTranslateVector(center, role.center);
                    translateVector.setX(-translateVector.getX());
                    translateVector.setY(-translateVector.getY());
                    motion(translateVector);
                } else {
                    Vector translateVector = PointUtil.getTranslateVector(center, role.center);
                    motion(translateVector);
                }

                // if (hp > 0.5f && hunger > 1) {
                //     Vector translateVector = PointUtil.getTranslateVector(center, collect.get(0).center);
                //     motion(translateVector);
                // } else {
                //     Role role = collect.get(0);
                //     Vector translateVector = PointUtil.getTranslateVector(center, role.center);
                //     translateVector.setX(-translateVector.getX());
                //     translateVector.setY(-translateVector.getY());
                //     motion(translateVector);
                // }
            } else {

                if (hp < 0.9 && hunger > 1) {
                    stop();
                } else {
                    Role roleSelect = null;
                    for (Role role : collect1) {
                        if (role.hp <= 0) {
                            roleSelect = role;
                            break;
                        }
                    }
                    if (roleSelect != null) {
                        Vector translateVector = PointUtil.getTranslateVector(center, roleSelect.center);
                        motion(translateVector);
                    } else {
                        stop();
                    }
                }
            }


            // if (collect1.get(0).hp <= 0) {
            //     Role role = collect1.get(0);
            //     Vector translateVector = PointUtil.getTranslateVector(center, role.center);
            //     motion(translateVector);
            //     return;
            // }

            // /* 逃跑 */
            // if (hp < 0.2 || (attackTime != null && time() - attackTime < 1000)) {
            //     Role role = collect1.get(0);
            //     Vector translateVector = PointUtil.getTranslateVector(center, role.center);
            //     translateVector.setX(-translateVector.getX());
            //     translateVector.setY(-translateVector.getY());
            //     if (translateVector.getLength() < role.size / 2 + size / 2 + 20) {
            //         motion(translateVector);
            //     } else {
            //         stop();
            //     }
            //     return;
            // }

            // Role role = collect1.get(0);
            // Vector translateVector = PointUtil.getTranslateVector(center, role.center);
            // motion(translateVector);
        }

        public static double calculateDotProduct(double[] vector1, double[] vector2) {
            double dotProduct = 0;
            for (int i = 0; i < vector1.length; i++) {
                dotProduct += vector1[i] * vector2[i];
            }
            return dotProduct;
        }

        public static double calculateAngleInRadians(double dotProduct, double[] vector1, double[] vector2) {
            double magnitude1 = calculateMagnitude(vector1);
            double magnitude2 = calculateMagnitude(vector2);
            double denominator = magnitude1 * magnitude2;
            if (denominator == 0) {
                throw new IllegalArgumentException("Vectors must be non-zero.");
            }
            return Math.acos(dotProduct / denominator);
        }

        public static double calculateMagnitude(double[] vector) {
            double magnitude = 0;
            for (double value : vector) {
                magnitude += value * value;
            }
            return Math.sqrt(magnitude);
        }

    }

    public static class Common {
        public static final float GAME_START_TIME = System.nanoTime() / 1000000.0f;
        public static final Random RANDOM = new Random();

        public static void setup() {

        }
    }

    public static abstract class Role implements Runnable {

        public String genealogy;
        public float hp; // hp
        public float size; // 体型大小
        public float speed; // 速度
        public float perceivedDistance; // 感知距离
        // public float attack; // 攻击力
        public float hunger; // 饱食度
        public float createTime; // 创建时间
        public final Set<Role> descendants; // 子孙列表
        private final FpsManager fpsManager;
        public Role parent; // 父母
        public Vector vector;
        public Point center;
        public Scene scene;
        public Color color;

        public Role() {
            this.genealogy = UUID.randomUUID().toString();
            this.hp = 1.0f;
            this.size = 15f;
            this.speed = 0f;
            this.perceivedDistance = 1000f;
            // this.attack = 20.0f;
            this.hunger = 1.0f;
            this.center = new Point(0, 0);
            this.createTime = time();
            this.descendants = new HashSet<>();
            this.fpsManager = new FpsManager();
        }

        @Override
        public final void run() {
            while (hp > 0.0f) {

                color = new Color(Math.round(size / 50 * 255), 10, 10);

                genealogyChange();

                translate();

                aroundRoleExec();

                exec();

                reply();

                if (hunger > 0.0f) {
                    hunger += hungerReduce();
                } else {
                    hp = 0f; // 饿死
                    break;
                }

                if (center.x > 0 && center.x < 1900 && center.y > 0 && center.y < 1000) {
                    production();
                }

                fpsManager.paintWait(60);
            }

            die();
        }

        private Float genealogyChangeTime;

        private void genealogyChange() {
            if (genealogyChangeTime == null) {
                genealogyChangeTime = time();
            }
            if (time() - genealogyChangeTime > 15000) {
                genealogy = UUID.randomUUID().toString();
                genealogyChangeTime = time();
            }
        }

        public Set<Role> aroundRole(float perceivedDistance) {
            if (scene != null) {

                return scene.roles.stream().filter(e -> {
                    if (e == this) {
                        return false;
                    }
                    if (genealogy.equals(e.genealogy)) {
                        return false;
                    }
                    LineSegment lineSegment = new LineSegment(this.center, e.center);
                    return Math.abs(lineSegment.length()) <= (e.size / 2.0f) + (size / 2.0f) + perceivedDistance;
                }).collect(Collectors.toSet());
            }
            return null;
        }

        public Float attackTime;

        /**
         * 攻击附近对象
         * 每秒攻击一次
         */
        private void aroundRoleExec() {
            Set<Role> roles = aroundRole(1);
            List<Role> collect = roles.stream().filter(e -> e.hp <= 0).collect(Collectors.toList());
            for (Role role : collect) {
                eat(role);
            }

            List<Role> collect1 = roles.stream().filter(e -> e.hp > 0).collect(Collectors.toList());

            if (!collect1.isEmpty()) {
                if (attackTime == null) {
                    for (Role role : collect1) {
                        if (role.underAttack(size / 15)) { // 攻击对象, 如果对象死亡则将它吃掉
                            eat(role);
                        }
                    }
                    attackTime = time();
                } else if (time() - attackTime > 1000) {
                    for (Role role : collect1) {
                        if (role.underAttack(size / 15)) {
                            eat(role);
                        }
                    }
                    attackTime = time();
                }
            }
        }

        /**
         * 受到攻击
         *
         * @param attack 攻击力
         * @return 返回true则代表实体死亡
         */
        public boolean underAttack(float attack) {
            if (hp <= 0.001) {
                return true;
            }

            final float defaultDefensive = 10.0f; // 默认防御力 10, 可以抵抗10点攻击
            final float defaultSize = 15f; // 默认大小为15(等于这个大小则可以达到默认防御力)
            // float v =  * defaultDefensive;
            // System.out.println("v = " + v + " size = " + size);
            hp -= defaultSize / this.size * 0.2f * attack;

            // hp -= Math.max(attack - v, 1.0f) * 0.5f;
            return hp <= 0;
        }

        private Float translateTime;

        private void translate() {
            if (speed > 0f && vector != null) {

                if (translateTime == null) {
                    translateTime = time();
                }
                float time = time();

                float v1 = time - translateTime;
                if (v1 > 0) {
                    float v2 = v1 / 1000 * speed;

                    double scale = PointUtil.getScale(vector.length(), v2);

                    double vx = vector.getX() * scale;
                    double vy = vector.getY() * scale;

                    center.setX(center.getX() + vx);
                    center.setY(center.getY() + vy);
                    translateTime = time;
                }
            }
        }

        private void die() {
            genealogy = UUID.randomUUID().toString();
            color = new Color(0, 255, 0);
        }

        private Float replyTime;
        private boolean reply = true;

        /**
         * 站着不动就可以恢复HP
         * 每秒恢复 '5%' 生命值
         */
        private void reply() {
            if (reply) {
                if (replyTime == null) {
                    replyTime = time();
                }

                float thatTime = time();
                float v = thatTime - replyTime;

                final float reduce = 0.2f;
                final float time = 1000;

                hp = Math.min(hp + v / time * reduce, 1f);
                replyTime = thatTime;
            }
        }

        public void motion(Vector vector) {
            final float defaultSpeed = 100.0f; // 默认速度20米每秒
            final float defaultSize = 15f; // 默认大小为15(等于这个大小则可以达到默认速度)

            this.vector = vector;
            this.speed = defaultSize / this.size * defaultSpeed;
            this.reply = false;
            this.replyTime = null;
        }

        public void stop() {
            this.speed = 0f;
            this.translateTime = null;
            this.reply = true;
        }

        private Float lastProductionTime;

        private void production() {
            if (lastProductionTime == null) {
                lastProductionTime = createTime;
            }
            if (time() - lastProductionTime > 20000) { // 每十秒可以生成一个孩子
                Role descendants1 = createDescendants();
                descendants.add(descendants1);
                scene.addRole(descendants1);
                lastProductionTime = time();
            }
        }

        public Role createDescendants() {
            try {
                Class<? extends Role> aClass = this.getClass();
                Constructor<? extends Role> constructor = aClass.getConstructor();
                Role role = constructor.newInstance();
                role.color = color;
                role.scene = scene;
                role.size = Math.min(Math.max(size * ((float) Common.RANDOM.nextDouble() * 0.4f + 0.8f), 5), 50); // 限制大小为 5 - 50
                role.parent = this;
                role.center = new Point(role.size / 2.0 + size / 2.0 + center.x + 50, center.y);
                return role;
            } catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
                     InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }

        private Float ReduceHungerTime;

        /**
         * 饱食度随着时间减少
         * 每两秒减少10%
         */
        private float hungerReduce() {

            final float reduce = -(size / 15 * 0.1f);
            final float time = 2000;
            float retFloat;

            if (this.ReduceHungerTime == null) {
                retFloat = reduce;
                this.ReduceHungerTime = time(); // 初始化时间
            } else if (time() - this.ReduceHungerTime > time) {
                retFloat = reduce;
                this.ReduceHungerTime = time(); // 初始化时间
            } else {
                retFloat = 0.0f;
            }

            return retFloat;
        }

        /**
         * 吃东西
         */
        public void eat(Role role) {
            hunger = role.size / size + hunger;
            scene.roles.remove(role);
        }

        /**
         * @return 返回游戏开始到现在所经过的时间
         */
        public static float time() {
            return System.nanoTime() / 1000000.0f - Common.GAME_START_TIME;
        }

        private Float loseHungerTime;

        /**
         * 如果饿了则每'两秒'减少'10%'生命值
         */
        private float calculateNextHpByLoseHunger() {
            final float reduce = -0.1f;
            final float time = 2000;

            float retFloat;

            if (this.loseHungerTime == null) {
                retFloat = reduce;
                this.loseHungerTime = time(); // 初始化时间
            } else if (time() - this.loseHungerTime > time) {
                retFloat = reduce;
                this.loseHungerTime = time(); // 初始化时间
            } else {
                retFloat = 0.0f;
            }

            return retFloat;
        }

        public abstract void exec();
    }
}
